/**
 * OPMERKINGEN OVER KLASSE
//TODO
 * De shipInteraction methodes herwerken naar this & de dubbele argumenten houden voor in facade en daar laten uitvoeren op bv ship1. Idem voor getTotalVelocity, zonder argumenten laten werken
 * MaxBounces moet int zijn, maar dan bestaat geen infinity voor ship en asteroid
 * Massa instellen
 *
 */

package asteroids.model;

import asteroids.Util;
import be.kuleuven.cs.som.annotate.*;
/**
 * 
 * @author 	Thomas & Carsten
 * @Date 	24/03
 *
 */
public abstract class CollisionObject {


	/**
	 * This new ship will be created with the given specifications in the arguments.
	 * @param xCoordinate				The current x-value of the ships center (2d)
	 * @param yCoordinate				The current y-value of the ships center (2d)
	 * @param xVelocity					The current rate at which the x-value changes
	 * @param yVelocity					The current rate at which the y-value changes
	 * @param radius					The distance between the center of the ship and the outer rim
	 * @param angle						The direction that the ship is pointing at (2d)
	 * @param mass 						The weight of the ship
	 * @throws IllegalRadiusException	If the given radius is smaller than the radiusconstraint, than the exception is thrown
	 * @throws OutOfBoundsException		If one (or both) of the coordinates aren't numbers, than the exception is thrown
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)==true && isValidRadius(radius) == true && isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			new.getXCoordinate() == xCoordinate
	 * 			new.getYCoordinate() == yCoordinate
	 * 			new.getXVelocity() == xVelocity
	 * 			new.getYVelocity() == yVelocity
	 * 			new.getRadius() == radius
	 * 			new.getAngle() == angle
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 * @invar	The mass of a ship has to be larger than 0
	 * 			|isValidMass(mass) == true		
	 */ 
	//MASS VERDER UITWERKEN MET DENSITY VS MASS ALS PARAMETER
	public CollisionObject (double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius)//,double angle,double mass) 
			throws OutOfBoundsException,IllegalRadiusException{

		if(isValidRadius(radius) == false){
			throw new IllegalRadiusException("That is an illegal radius");
		}
		setCoordinate(xCoordinate, yCoordinate);
		//setXCoordinate(xCoordinate);
		//setYCoordinate(yCoordinate);
		//velocityConstraint = createVelocityConstraint(getSpeedOfLight());
		setValidVelocity(xVelocity,yVelocity);


		//setDensity(density);
		setMaximumMass();
		setMinimumMass();
		setMass( mass);
		this.radius=radius;	
		this.state = State.NOWORLD;
	}
	private int getXPositionInVector()
	{
		return this.xPositionInVector;
	}
	private int getYPositionInVector(){
		return this.yPositionInVector;
	}
	private final int xPositionInVector = 0;
	private final int yPositionInVector = 1;

	/**
	 * Get the x-coordinate from this CollisionObject
	 * @return The x-coordinate of this collisionobject.
	 */
	@Basic
	public double getXCoordinate() {
		return positionVector.getValueAtPosition(getXPositionInVector());	
	}	

	/**
	 * 
	 * @param xCoordinate				The current x-value of the collisionobject center (2d)
	 * @throws OutOfBoundsException		If the xCoordinate isn't a number, than the exception is thrown
	 * 									|!isValidCoordinate
	 * @post (new this).getXCoordinate() == xCoordinate
	 */
	public void setXCoordinate(double xCoordinate) throws OutOfBoundsException{
		if(isValidCoordinate(xCoordinate) == true){
			//positionVector.replaceValueAtPosition(getXPositionInVector(), xCoordinate);
			positionVector = new Vector(xCoordinate, getYCoordinate());
		}
	}

	@Basic
	public double getYCoordinate() {
		return positionVector.getValueAtPosition(getYPositionInVector());
	}

	/**
	 * @param yCoordinate				The current y-value of the ships center (2d)
	 * @throws OutOfBoundsException		If the yCoordinate isn't a number, than the exception is thrown
	 * @post (new this).getYCoordinate() == yCoordinate
	 */
	public void setYCoordinate(double yCoordinate) throws OutOfBoundsException{
		if (isValidCoordinate(yCoordinate) == true){
			positionVector = new Vector(yCoordinate, getXCoordinate()); // TODO juist?
		}
	}
	public void setCoordinate(double xCoordinate, double yCoordinate){
		if (isValidCoordinate(xCoordinate)  && isValidCoordinate(yCoordinate)){
			positionVector = new Vector(xCoordinate,yCoordinate);
		}
	}

	/**
	 * 
	 * @param duration	The time that the movement took
	 * @post	if the duration is a valid value, then the ship will move a certain amount,
	 * 			based on the current position and the velocity 
	 * 			|(new this).getXCoordinate() == xCoordinate + xVelocity*checkDuration(duration)
	 * 			|(new this).getYCoordinate() == yCoordinate + yVelocity*checkDuration(duration) 
	 */
	public void move(double duration) {
		setXCoordinate(getXCoordinate() + getXVelocity()*checkDuration(duration));
		setYCoordinate(getYCoordinate() + getYVelocity()*checkDuration(duration));
	}

	/**
	 * 
	 * @param coordinate				The coordinate in the 2d space
	 * @throws OutOfBoundsException		If the coordinate isn't a number, than the exception is thrown
	 * @post	this method checks if a method is a number, and throws an exception if that is not the case
	 * 			|if(Double.isNaN(coordinate)){throw new OutOfBoundsException("That is an invalid coordinate")}
	 * 			|else result == true
	 */
	@Model
	protected boolean isValidCoordinate(double coordinate) throws OutOfBoundsException{
		if(Double.isNaN(coordinate)){throw new OutOfBoundsException("That is an invalid coordinate");}
		else return true;

	}

	private Vector getPositionVector(){
		return this.positionVector;
	}

	private Vector positionVector;

	@Basic
	public double getXVelocity(){
		return velocityVector.getValueAtPosition(getXPositionInVector());
	}

	/**
	 * 
	 * @param xVelocity The new xVelocity 
	 * @post			If it is a valid velocity, then the given xVelocity is the new xVelocity
	 * 					|if(isValidVelocity(xVelocity, this.getYVelocity())){
						|(new this).getXVelocity = xVelocity;
	 */
	public void setXVelocity(double xVelocity){
		if(isValidVelocity(xVelocity, this.getYVelocity())){
			velocityVector.replaceValueAtPosition(getXPositionInVector(), xVelocity);
		}
	}

	@Basic //TODO
	public double getYVelocity(){
		return velocityVector.getValueAtPosition(getYPositionInVector());
	}

	/**
	 * 
	 * @param yVelocity The new yVelocity 
	 * @post			If it is a valid velocity, then the given yVelocity is the new yVelocity
	 * 					|if(isValidVelocity(this.getXVelocity(), yVelocity)){
						|(new this).getYVelocity = yVelocity;
	 */
	public void setYVelocity(double yVelocity){
		if(isValidVelocity(this.getXVelocity(), yVelocity)){
			velocityVector.replaceValueAtPosition(getYPositionInVector(), yVelocity);
		}
	}

	/**
	 * 
	 * @param xVelocity		The current rate at which the x-value changes
	 * @param yVelocity		The current rate at which the y-value changes
	 * @post 	This method sets the new velocity bringing into account that the velocity can't exceed 300000 km/s; if it does, the velocity becomes zero.
	 * 			|if(isValidVellocity(xVelocity,yVelocity) == false){
	 * 			|			(new this).getXVelocity() == 0
	 * 			|			(new this).getYVelocity() == 0}
	 * @post	|If the velocity doesn't exceed 300000 km/s then the given velocity becomes the new velocity.
	 * 			|if(isValidVellocity(xVelocity,yVelocity) == true){
	 * 			|			(new this).getXVelocity() == xVelocity
	 * 			|			(new this).getYVelocity() == yVelocity}
	 */	 	
	protected void setValidVelocity(double xVelocity,double yVelocity){
		if(!isValidVelocity(xVelocity,yVelocity)){
			setXVelocity(0);setYVelocity(0);
		}
		else {
			velocityVector = new Vector(xVelocity,yVelocity);
			//setXVelocity(xVelocity);
			//setYVelocity(yVelocity);
		}
	}

	/**
	 * 
	 * @param xVelocity		The current rate at which the x-value changes
	 * @param yVelocity		The current rate at which the y-value changes
	 * @return	True if the absolute velocity is less then 300000 km/s
	 * 			|if(Math.sqrt(xVelocity^2 + yVelocity^2) <= 3000000)){result == true}		 
	 */
	protected boolean isValidVelocity(double xVelocity, double yVelocity){
		if(	(Util.fuzzyLessThanOrEqualTo(getTotalVelocity(xVelocity, yVelocity), getVelocityConstraint())) == true)
			return true;
		return false;
	}
	//Mss abstract maken om verder nog te gebruiken
	private double createVelocityConstraint(double velocity){
		if(velocity < getSpeedOfLight()){
			return velocity;
		}
		return getSpeedOfLight();
	}

	public double getTotalVelocity(double xVelocity, double yVelocity){
		return Math.sqrt(Math.pow(xVelocity,2) + Math.pow(yVelocity,2));
	}

	protected double getVelocityConstraint(){
		return this.velocityConstraint;
	}

	@Immutable
	public double getSpeedOfLight(){
		return this.SPEED_OF_LIGHT;
	}

	private Vector getVelocityVector(){
		return this.velocityVector;
	}

	public void setVelocityAfterObjectCollsion(CollisionObject collisionObject){
		//TODO
		double oldXVelocityObject1,oldXVelocityObject2,oldYVelocityObject1,oldYVelocityObject2, jParam, jX, jY, newXVelocityObject1, newYVelocityObject1, newXVelocityObject2, newYVelocityObject2;


		//INITIALISATION TEMP VAR
		//THIS OBJECT = OBJECT1: SPECIFICATIONS@STATE
		oldXVelocityObject1 	= this.getXVelocity();
		oldYVelocityObject1 	= this.getYVelocity();
		double massObject1 		= this.getMass();
		double radiusObject1 	= this.getRadius();

		//OBJECT 2 = COLLISIONOBJECT: SPECIFICATIONS@STATE
		oldXVelocityObject2 	= collisionObject.getXVelocity();
		oldYVelocityObject2 	= collisionObject.getYVelocity();
		double massObject2 		= collisionObject.getMass();
		double radiusObject2 	= collisionObject.getRadius();

		//RESULTING FROM BOTH OBJECTS
		double totalRadius 		= radiusObject1+radiusObject2;
		double totalMass 		= massObject1 + massObject2;
		// das weer een berekening dus ik denk da  der nei veel aan te doen is
		//TODO codeduplicatie van getTimeToCollision ~ echt abstraheren? Ik denk het niet want dit zouden zogezegd twee totaal verschillende methodes kunnen zijn... evt deltaV etc maar echt nuttig is het niet
		double deltaX 			= collisionObject.getXCoordinate()-this.getXCoordinate();
		double deltaY			= collisionObject.getYCoordinate()-this.getYCoordinate();
		double deltaXVelocity 	= collisionObject.getXVelocity() - this.getXVelocity();
		double deltaYVelocity 	= collisionObject.getYVelocity() - this.getYVelocity();
		double deltaVTimesDeltaR;


		if(	//CHECK WHETHER NO ISNAN OR INFINITY
				isCalculateableDoubleRange(deltaX) 			&& isCalculateableDoubleRange(deltaY) 
				&& 	isCalculateableDoubleRange(deltaXVelocity) 	&& isCalculateableDoubleRange(deltaYVelocity)
				&& 	isCalculateableDoubleRange(totalMass) 		&& isCalculateableDoubleRange(totalRadius)
				){
			//"VECTORCREATION" NOT A REAL VECTOR FROM DEFINED CLASS SINCE JUST NEED FOR TEMP USAGE. IF CALCULATEDOTPRODUCT IS MOVED TO THE CLASS VECTOR THESE SHOULD BE VECTORS
			double[] deltaR = {deltaX,deltaY};
			double[] deltaV = {deltaXVelocity,deltaYVelocity};
			//RESULT FROM DOTPRODUCT
			deltaVTimesDeltaR = calculateDotProduct(deltaV, deltaR);
		}
		else{//THROWN IF INCALCULATABLE
			throw new IncalculateableRangeException("There was a calculation which isn't applicable for further usage");
		}

		//FURTHER CALCULATIONS FOR RESULT
		jParam 	= 2*this.getMass()*collisionObject.getMass()*deltaVTimesDeltaR/totalRadius/totalMass;
		jX		= jParam*deltaX / totalRadius;
		jY		= jParam*deltaY / totalRadius;

		//RESULTING VELOCITIES
		newXVelocityObject1 =	jX/massObject1 	+	oldXVelocityObject1;
		newYVelocityObject1 =	jY/massObject1 	+	oldYVelocityObject1;
		newXVelocityObject2 =	-jX/massObject2 + 	oldXVelocityObject2;
		newYVelocityObject2	=	-jY/massObject2 +	oldYVelocityObject2;

		//SETTING THE VELOCITY
		//TODO Abstraheren naar kleinere methodes, code duplicatie

		//OBJECT1
		if(isValidVelocity(newXVelocityObject1, newYVelocityObject1)){
			this.setXVelocity(newXVelocityObject1);
			this.setYVelocity(newYVelocityObject1);
		}
		else{
			double vectorAngle1 = getResAngleOfPerpendicularVectors(newXVelocityObject1, newYVelocityObject1);
			this.setXVelocity(getSpeedOfLight()*Math.cos(vectorAngle1));
			this.setYVelocity(getSpeedOfLight()*Math.sin(vectorAngle1));
		}

		//OBJECT2
		if(isValidVelocity(newXVelocityObject2, newYVelocityObject2)){
			this.setXVelocity(newXVelocityObject2);
			this.setYVelocity(newYVelocityObject2);
		}
		else{
			double vectorAngle2 = getResAngleOfPerpendicularVectors(newXVelocityObject2, newYVelocityObject2);
			this.setXVelocity(getSpeedOfLight()*Math.cos(vectorAngle2));
			this.setYVelocity(getSpeedOfLight()*Math.sin(vectorAngle2));
		}
	}

	public static double getResAngleOfPerpendicularVectors(double xVectorSize, double yVectorSize){
		double angle = Math.atan2(yVectorSize, xVectorSize);
		return angle;
	}


	public void setVelocityAfterBoundaryCollision(){
		if(isLeftRightCollision()){
			setXVelocity(-getXVelocity());
		}
		if(isUpperLowerCollision()){
			setYVelocity(-getYVelocity());
		}
	}

	/**
	 * Whether or not the object collide with the left or the right boundary (NOT TO CHECK WHICH SIDE OF THOSE TWO!!)
	 */
	private boolean isLeftRightCollision(){
		if(this.getXCoordinate() == this.world.getLeftBoundary() + this.getRadius() || this.getXCoordinate() == this.world.getWorldWidth() - this.getRadius() ){
			return true;
		}
		//TODO
		return false;
	}

	/**
	 * Whether or not the object collide with the upper or lower boundary (NOT TO CHECK WHICH SIDE OF THOSE TWO!!)
	 * @return
	 */
	private boolean isUpperLowerCollision(){
		if(this.getYCoordinate() == this.world.getLowerBoundary() + this.getRadius() || this.getXCoordinate() == this.world.getWorldHeight()-this.getRadius()){
			return true;
		}
		//TODO
		return false;
	}

	private double getTimeToBoundaryCollision(){
		double result =Double.MAX_VALUE;
		double radius = this.getRadius();
		if(Util.fuzzyLessThanOrEqualTo(getXVelocity(), 0)){
			if(Util.fuzzyEquals(getXVelocity(), 0)){
			}
			else{result = (this.world.getLeftBoundary()-getXCoordinate() + radius)/getXVelocity();
			}
		}
		else{result = (this.world.getRightBoundary() - getXCoordinate() - radius)/getXVelocity();

		}
		if(Util.fuzzyLessThanOrEqualTo(getXVelocity(), 0)){
			double yResult = Double.MAX_VALUE;
			if(Util.fuzzyEquals(getYVelocity(), 0)){
			}
			else{yResult = (this.world.getLowerBoundary() -getYCoordinate() + radius)/getYVelocity();
			}
		}
		else{result = (this.world.getUpperBoundary() - getYCoordinate() - radius)/getYVelocity();

		}	
		return result;
	}





	private final double SPEED_OF_LIGHT = 300000;
	private final double velocityConstraint = SPEED_OF_LIGHT;
	private Vector velocityVector;








	@Basic
	public double getRadius(){
		return this.radius;
	}

	/**
	 * 
	 * @param radius						The distance between the center of the ship and the outer rim
	 * @throws	IllegalRadiusException		If the given radius is smaller than the radiusconstraint, than the exception is thrown
	 * @return 	true if the value of the radius is a situated between the minimum and maximumradiussize.
	 * 			|if(radius >= radiusConstraint && radius<= maxRadiusConstraint && radius < Double.POSITIVE_INFINITY){result == true}
	 */	
	public boolean isValidRadius(double radius){
		if(Util.fuzzyLessThanOrEqualTo(radius, getMinRadiusConstraint()) || !Util.fuzzyLessThanOrEqualTo(radius, getMaxRadiusConstraint())|| Util.fuzzyEquals(radius, Double.POSITIVE_INFINITY) || Double.isNaN(radius)){
			return false;
		}
		return true;
	}

	/**
	 * Get the minimumRadiusSize
	 * @return
	 */
	@Basic
	private double getMinRadiusConstraint(){
		return this.minRadiusConstraint;
	}
	private double getMaxRadiusConstraint(){
		return this.maxRadiusConstraint;
	}
	//IK ZOU DEZE SETTEN IN DE CONSTRUCTOR EN HIER DE METHODES SETMINRADIUS EN SETMAXRADIUS ABSTRACT MAKEN
	private final double radius;
	private final double minRadiusConstraint = 0; //TODO
	private final double maxRadiusConstraint = Double.POSITIVE_INFINITY;

	//	/**
	//	 * Set the mass of this spaceobject
	//	 * @param mass						The mass you want to give to the object
	//	 * @throws IllegalMassException 	Is thrown if the given mass is not a valid mass.
	//	 * 									|!isValidMass(mass)
	//	 * @post	The mass of this collisionobject will become equal to the given mass.
	//	 * 			| new.getMass() == mass
	//	 */
	//	public void setMass(double mass) throws IllegalMassException {
	//		if(isValidMass(mass) == true)
	//		{
	//			this.mass = mass;
	//		}
	//		else {
	//			throw new IllegalMassException("That is an invalid mass");
	//		}
	//	}
	//	



	@Basic @Raw
	public double getMass() {
		return this.mass;
	}

	//IK ZOU SETMASS VERPLAATSEN NAAR HET OBJECT ZELF
	//

	/**
	 * Check whether the mass is valid.
	 * @param mass
	 * @return 	True if the mass is situated between the maximum-mass and the lowest-mass allowed.
	 * 			| 
	 */
	protected  void setMass(double mass){
		if(isValidMass(mass))
			this.mass = mass;
		throw new IllegalValueException("That mass is not allowed");
	}

	//MIN EN MAXMASS OMWILLE VAN EVENTUELE UITBREIDBAARHEID
	protected boolean isValidMass(double mass){
		if(Util.fuzzyLessThanOrEqualTo(mass, getMinimumMass()) || !Util.fuzzyLessThanOrEqualTo(mass, getMaximumMass())|| Double.isNaN(mass)){
			return false;
		}
		return true;
	}

	protected abstract double getMinimumMass();
	protected abstract double getMaximumMass();
	//protected abstract double getMassToAssign();

	private void setMinimumMass(){
		this.minimumMass = getMinimumMass();
	}

	private void setMaximumMass(){
		this.maximumMass = getMaximumMass();
	}

	//TODO deze velden worden niet gebruikt omdat dit allemaal in de subklassen gebeurd 
	protected double mass;
	private double maximumMass;
	private double minimumMass;






	/**
	 * 
	 * @param duration	The time that a method gets
	 * @return	if the given duration is larger then zero, then result is duration
	 * 			otherwise the duration becomes zero
	 * 			|if(duration>0){result == true}
	 */
	//Mss beter renamen naar isValidDuration en in de methode zelf de aanpassing maken naar 0;
	private double checkDuration(double duration){
		if(	Util.fuzzyLessThanOrEqualTo(duration, 0) ==true)
			return 0;
		return duration;
	}

	/**
	 * 
	 * @param collisionObject1 The first ship to use as reference to calculate the distance with the other ship.
	 * @param collisionObject2 The second ship to use as another object in space to calculate the distance.
	 * @return 	The absolute distance between the two inserted ships: collisionObject1 & ship 2. 
	 * 			| math.sqrt(Math.pow(collisionObject2.getXCoordinate()-collisionObject1.getXCoordinate())+ Math.pow(collisionObject2.getYCoordinate()-collisionObject1.getYCoordinate()))
	 * @return If the distance between the 2 ships is infinite the method returns the greatest representable double value in Java.
	 * 			| if(getDistanceBetween(collisionObject1,collisionObject2)== Double.POSITIVE_INFINITY){
	 * 				return Double.MAX_VALUE}
	 * @throws IllegalShipException
	 * @throws IncalculateableRangeException 
	 */

	public  double getDistanceBetween(CollisionObject collisionObject) throws IllegalShipException, IncalculateableRangeException {
		if(this==collisionObject)
			return 0;
		if(!isValidCollsionObjectCombination(collisionObject)){
			throw new IllegalShipException("Invalid shipobject");
		}

		double xDiffToTheSecond = Math.pow(getXDistanceBetween(this, collisionObject), 2);
		double yDiffToTheSecond = Math.pow(getYDistanceBetween(this, collisionObject), 2);

		double sum = xDiffToTheSecond + yDiffToTheSecond;
		if(sum == Double.POSITIVE_INFINITY)
			throw new IncalculateableRangeException("There was a calculation which was not usefull anymore for further calculation");
		double result = Math.sqrt(sum);
		return result;
	}

	private  double getXDistanceBetween(CollisionObject collisionObject1,CollisionObject collisionObject2){
		return collisionObject1.getXCoordinate() - collisionObject2.getXCoordinate();
	}

	private  double getYDistanceBetween(CollisionObject collisionObject1,CollisionObject collisionObject2){
		return collisionObject1.getYCoordinate()-collisionObject2.getYCoordinate();
	}




	/**
	 * 
	 * @param collisionObject	The second ship to check whether there is an overlap with the first ship.
	 * @return 	true if the two ships make contact in space.
	 * 			false if the two ships do not make contact in space.
	 * 			| return getDistanceBetween(collisionObject1,collisionObject2) <= collisionObject1.getRadius()+collisionObject2.getRadius()
	 * @throws IllegalShipException 
	 * @throws IllegalCalculateableRangeException 
	 */
	public  boolean overlap(CollisionObject collisionObject) throws IllegalShipException, IncalculateableRangeException{
		if(this == collisionObject){
			return true;
		}
		double totalDistance = getDistanceBetween(collisionObject);
		double totalRadius = this.getRadius()+collisionObject.getRadius();
		if(totalDistance <= totalRadius){
			return true;
		}
		return false;
	}

	/**
	 * 
	 * @param collisionObject1 The first ship where from you want to know the time until collision with another ship.
	 * @param collisionObject2 The second ship where from you want to know if it will collide with collisionObject1.
	 * @return Returns the time, in seconds, until the collision between collisionObject1 and collisionObject2, if the two ships will collide.
	 * 		   
	 * @return Returns The highest double that is represented in Java if the both ships are not on collision course
	 * 					|collisionObject1.move(getTimeToCollision());
	 * 					|collisionObject2.move(getTimeToCollision());
	 * 					|collisionObject1.overlap(collisionObject1,collisionObject2) == true;
	 * 			
	 * @throws IllegalVectorProductException
	 * @throws IncalculateableRangeException
	 * @throws IllegalRadiusException
	 * @throws IllegalShipException
	 */
	public  double getTimeToCollision(CollisionObject collisionObject) throws IllegalVectorProductException,
	IncalculateableRangeException, IllegalRadiusException, IllegalShipException{	

		if(!isValidCollsionObjectCombination(collisionObject)){
			throw new IllegalShipException("Invalid shipobject");
		}


		double deltaX = collisionObject.getXCoordinate()-this.getXCoordinate();
		double deltaY = collisionObject.getYCoordinate()-this.getYCoordinate();

		//Alle verschil in snelheden
		double deltaXVelocity = collisionObject.getXVelocity() - this.getXVelocity();
		double deltaYVelocity = collisionObject.getYVelocity() - this.getYVelocity();
		if(isCalculateableDoubleRange(deltaX) && isCalculateableDoubleRange(deltaY) && 
				isCalculateableDoubleRange(deltaXVelocity) && isCalculateableDoubleRange(deltaYVelocity)){
			//VectorCreation
			double[] deltaR = {deltaX,deltaY};
			double[] deltaV = {deltaXVelocity,deltaYVelocity};
			double sigmaTotal = this.getRadius() + collisionObject.getRadius();
			if(sigmaTotal == Double.POSITIVE_INFINITY)
				throw new IllegalRadiusException("The sum of the two radia is not applicable for calculation");

			//Results
			double deltaVTimesDeltaR = calculateDotProduct(deltaV, deltaR);
			double deltaVTimesDeltaV = calculateDotProduct(deltaV,deltaV);
			double deltaRTimesDeltaR = calculateDotProduct(deltaR, deltaR);

			//Calculate d from endresult in formula
			double part1 = deltaVTimesDeltaR*deltaVTimesDeltaR;
			double part2 = (sigmaTotal*sigmaTotal - deltaRTimesDeltaR)*deltaVTimesDeltaV;
			double dParam = part1+part2;

			//Resultsplitting
			if (deltaVTimesDeltaR >= 0){
				return Double.POSITIVE_INFINITY;
			}
			if (dParam <= 0){
				return Double.POSITIVE_INFINITY;
			}
			else{
				return -(deltaVTimesDeltaR+Math.sqrt(dParam))/deltaVTimesDeltaV;			
			}
		}
		else
			throw new IncalculateableRangeException("There was a calculation which isn't applicable for further usage");
	}

	/**
	 * 
	 * @param vector1
	 * @param vector2
	 * @return Returns the dotproduct of two vectors of equal length. If their length isn't equal.
	 * @throws IllegalVectorProductException Is thrown when the two vectors are of inequal length
	 * @throws IncalculateableRangeException
	 */
	private  double calculateDotProduct(double[] vector1, double[] vector2) throws IllegalVectorProductException, IncalculateableRangeException{
		double result=0;
		if (!isValidVectorCombination(vector1, vector2))
			throw new IllegalVectorProductException("Those vectors aren't possible for a dotproduct, invalid combination");

		for(int counter=0; counter  <vector1.length; counter++){
			double tempResult= vector1[counter]*vector2[counter];
			result += tempResult;
		}
		if (result == Double.POSITIVE_INFINITY||result==Double.NEGATIVE_INFINITY)
			throw new IncalculateableRangeException("Those vectors aren't possible for a dotproduct, out of range");
		return result;
	}


	private  boolean isValidVectorCombination(double[] vector1, double[] vector2){
		return (vector1.length==vector2.length && vector1.length!=0);
	}	

	/**
	 * 
	 * @param collisionObject1 The first vessel to compare with collisionObject2 to get the collision position between collisionObject1 and collisionObject2.
	 * @param collisionObject2 The vessel that was compared with collisionObject1 to get the collision position between collisionObject1 and collisionObject2.
	 * @return 	The x- and y-coordinate from the potential collision between collisionObject1 and collisionObject2.
	 *
	 * 			| return {collisionObject1.getXVelocity()*getTimeToCollision(collisionObject1,collisionObject2)+collisionObject1.getXPosition(), collisionObject1.getYVelocity()*getTimeToCollision(collisionObject1,collisionObject2)+collisionObject1.getYPosition(collisionObject1,collisionObject2)}
	 * @throws IllegalVectorProductException
	 * @throws IncalculateableRangeException
	 * @throws IllegalRadiusException
	 * @throws IllegalShipException
	 */
	//moet er nog een if statement in de @return aangezien deze al bij 
	//geen nullpointerexception doen want die wordt uitgevoerd bij getTimeToCollision
	public  double[] getCollisionPosition(CollisionObject collisionObject) throws IllegalVectorProductException,
	IncalculateableRangeException, IllegalRadiusException, IllegalShipException{

		double timeLapse = getTimeToCollision(collisionObject);
		if(timeLapse != Double.POSITIVE_INFINITY){
			double collisionAngle = getCollisionAngle(getObjectAtColissionPosition(this, timeLapse), getObjectAtColissionPosition(collisionObject, timeLapse));
			if(getTotalVelocity(this)!=0){
				return getAlternCollisionPosition(this, timeLapse, collisionAngle);
			}
			if(getTotalVelocity(collisionObject)!=0)
			{
				return getAlternCollisionPosition(collisionObject, timeLapse, collisionAngle);
			}
			else
				return null;
		}
		else
			throw new IncalculateableRangeException("The ships will not collide");

	}

	// TODO klopt dees nog?
	private  double getCollisionAngle(CollisionObject collisionObject1, CollisionObject collisionObject2){
		double x = getXDistanceBetween(collisionObject1, collisionObject2);
		double y = getYDistanceBetween(collisionObject1, collisionObject2);
		return Math.atan2(y, x);
	}

	private  double[] getAlternCollisionPosition(CollisionObject collisionObject, double timeLapse, double collisionAngle){
		double xCollisionPosition,yCollisionPosition,xRadiusExtra,yRadiusExtra;

		xRadiusExtra = collisionObject.getRadius()*Math.cos(collisionAngle);
		yRadiusExtra = collisionObject.getRadius()*Math.sin(collisionAngle);

		xCollisionPosition = collisionObject.getXVelocity()*timeLapse + collisionObject.getXCoordinate()+xRadiusExtra;
		yCollisionPosition = collisionObject.getYVelocity()*timeLapse + collisionObject.getYCoordinate()+yRadiusExtra;

		double[] collisionLocation = {xCollisionPosition,yCollisionPosition};
		return collisionLocation;
	}

	private  boolean isValidCollsionObjectCombination(CollisionObject collisionObject){
		return (collisionObject!=null && this != collisionObject);
	}

	private  double getTotalVelocity(CollisionObject collisionObject){
		return Math.sqrt(Math.pow(collisionObject.getXVelocity(),2)+Math.pow(collisionObject.getYVelocity(), 2));
	}

	private  boolean isCalculateableDoubleRange(double value) throws IncalculateableRangeException{
		if(value == Double.POSITIVE_INFINITY || value == Double.NEGATIVE_INFINITY || Double.isNaN(value)){
			throw new IncalculateableRangeException("The value isn't applicable for further usage");
		}
		return true;
	}

	//DEZE ABSTRACT MAKEN EN VERPLAATSEN NAAR SUBKLASSES
	//	private  CollisionObject getShipAtColissionPosition(CollisionObject collisionObject,double timeLapse){
	//		double newXCoordinate = collisionObject.getXVelocity()*timeLapse + collisionObject.getXCoordinate();
	//		double newYCoordinate = collisionObject.getYVelocity()*timeLapse + collisionObject.getYCoordinate();
	//		CollisionObject resultCollisionObject = new CollisionObject(newXCoordinate,newYCoordinate,collisionObject.getXVelocity(),collisionObject.getYCoordinate(),collisionObject.getRadius(),collisionObject.getAngle(),collisionObject.getMass());
	//		return resultCollisionObject;
	//	}
	protected abstract CollisionObject getObjectAtColissionPosition(CollisionObject collisionObject,double timeLapse);










	public boolean isShip(){
		if(Ship.class.isInstance(this))
			return true;
		return false;
	}

	/**
	 * Check whether <code>o</code> is an asteroid.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	public boolean isAsteroid(){
		if(Asteroid.class.isInstance(this))
			return true;
		return false;
	}

	/**
	 * Check whether <code>o</code> is a bullet.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	public boolean isBullet(){
		if(Bullet.class.isInstance(this))
			return true;
		return false;
	}



	public abstract void collisionReactionWith(CollisionObject collisionObject);













	private static enum State {
		INWORLD, NOWORLD, DESTROYED;
	}

	private State state;

	@Raw @Basic
	private State getState() {
		return this.state;
	}

	private void setState(State state) {
		assert (state != null);
		this.state = state;
	}

	private boolean hasProperState(){
		return isDestroyed() ^ isInNoWorld() ^ isInWorld();
	}

	@Basic
	public boolean isDestroyed(){
		return this.getState() == State.DESTROYED;
	}

	@Basic
	public boolean isInNoWorld(){
		return this.getState() == State.NOWORLD;
	}

	@Basic
	public boolean isInWorld(){
		return this.getState() == State.INWORLD;
	}

	public void destroy(){
		if(isInWorld()){//!isDestroyed()){
			setState(State.DESTROYED);
			if(this.getWorld() != null){
				World oldWorld = this.getWorld();
				setWorld(null);
				oldWorld.removeAsCollisionObject(this);
			}
			//			Vector oldPositionVector = this.getPositionVector();
			//			Vector oldVelocityVector = this.getVelocityVector();
			this.positionVector = null;
			this.velocityVector = null;
		}
	}



	public boolean exceedsWorldBoundaries() {
		if(this.getState() != State.INWORLD)
			throw new IllegalStateException();
		//TODO Ik weet niet goed hoe het veld opgebouwd is (of het linksonder begint met de oorsprong, of de oorsprong in het midden ligt,...), de onderstaande methodes met error zouden nog in world moeten komen. 
		if(
				this.getXCoordinate() + this.getRadius() > this.getWorld().getRightBoundary()
				||this.getXCoordinate() - this.getRadius() < this.getWorld().getLeftBoundary()
				||this.getYCoordinate() + this.getRadius() > this.getWorld().getUpperBoundary()
				|| this.getYCoordinate() + this.getRadius() < this.getWorld().getLowerBoundary()
				)
			return true;
		return false;

	}






	@Raw
	public boolean canHaveAsWorld(World world){
		if(this.isDestroyed())
			return world == null;
		return (world!=null && world.canHaveAsCollisionObject(this));
	}

	public void setWorld(World world) throws IllegalStateException{

		if(this.getState() == State.NOWORLD){
			this.world = world;
			world.addAsCollisionObject(this);}
		else{
			throw new IllegalStateException();
		}
	}

	public World getWorld(){
		return this.world;
	}

	private boolean hasValidWorld(){
		State state = this.getState();
		if(state == State.DESTROYED)
			return state == null;
		if(state == State.NOWORLD)
			return state == null;
		if(state == State.INWORLD)
			return (world!=null && this.getWorld().canHaveAsCollisionObject(this) && this.canHaveAsWorld(world) && hasProperState());
		return false;
	}

	private World world;

	private boolean isValidObject(){
		return (isValidCoordinate(this.getYCoordinate()) && isValidCoordinate(this.getXCoordinate()) && isValidMass(this.getMass()) && isValidRadius(this.getRadius()) && isValidVelocity(this.getXVelocity(), this.getYVelocity()) && hasValidWorld() && hasProperState());
	}


	public void bounceOff(){


	}
	//TODO Weten hoe veld opgebouwd is.. Ook gebruikt in Asteroid met collisionInteraction;
	//nope dees is voor als twee asteroids botsen denk ik
	// @Thomas #TODO work instead of reading stupid shit




}